Skip to content

fix(test-helpers): load the C runtime portably instead of hard-coding libc.so.6 - #2562

Open
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:helpers-portable-libc-load
Open

fix(test-helpers): load the C runtime portably instead of hard-coding libc.so.6#2562
LeSingh1 wants to merge 1 commit into
NVIDIA:mainfrom
LeSingh1:helpers-portable-libc-load

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

I'll state the counter-argument up front: cuda_python_test_helpers/pyproject.toml declares Operating System :: POSIX :: Linux, so failing on macOS is arguably out of contract and you may reasonably decline this. Two things are still true regardless: musl (Alpine) is Linux and has no libc.so.6 either, and the cost of the current behaviour is that the entire cuda_core suite cannot be collected rather than one helper being unavailable. Your call.

Problem

cuda_python_test_helpers/__init__.py loads the C runtime at import time:

if IS_WINDOWS:
    libc = ctypes.CDLL("msvcrt.dll")
else:
    libc = ctypes.CDLL("libc.so.6")

libc.so.6 is the glibc soname specifically — it does not exist on musl or macOS:

>>> import cuda_python_test_helpers
OSError: dlopen(libc.so.6, 0x0006): tried: 'libc.so.6' (no such file),
  '/System/Volumes/Preboot/Cryptexes/OSlibc.so.6' (no such file),
  '/usr/lib/libc.so.6' (no such file, not in dyld cache), 'libc.so.6' (no such file)

Three things make that more expensive than it first looks.

1. The file already computes the right predicate and does not use it. Four lines above:

IS_LINUX: bool = not IS_WINDOWS and not IS_WSL and platform.system() == "Linux"

The else branch covers every non-Windows platform, not Linux. This is the same validation-asymmetry shape as the numa_id / is_numa_current split in Host — the correct check exists and the guard next to it doesn't use it.

2. Exactly one function needs the library. libc is used only for memcmp, in cuda_core/tests/helpers/buffers.py (two call sites). Everything else the package exports — IS_WSL, IS_LINUX, IS_WINDOWS, under_compute_sanitizer, driver_version_less_than — is pure Python.

3. It breaks collection, not just that helper. cuda_core/tests/conftest.py registers this package as a pytest plugin:

pytest_plugins = ["cuda_python_test_helpers._pytest_plugin"]

so an unloadable libc stops the whole cuda_core suite from being collected, including every test that never touches libc.

Fix

Try the glibc soname first — so the library resolved on the platforms CI runs on is bit-for-bit what it was before — and fall back to ctypes.util.find_library("c"). If nothing loads, raise an OSError naming the platform and what was tried, rather than a raw dlopen dump.

On a glibc host this is a no-op: CDLL("libc.so.6") succeeds on the first candidate and find_library is never called.

Tests

cuda_python_test_helpers has no test directory today; this adds cuda_python_test_helpers/tests/test_libc_loading.py:

  • libc exposes a working memcmp (equal and differing inputs);
  • test_import_survives_without_the_glibc_soname — monkeypatches ctypes.CDLL to reject "libc.so.6" and re-imports the package, simulating musl/macOS on any host. This is the regression test, and it fails on main on a glibc runner too, so it has teeth in CI rather than only on my machine;
  • a genuinely unloadable C runtime still raises, with a message naming what was tried.

The fixture restores the real ctypes.CDLL and re-imports the package on teardown, so the reload does not leak into other tests.

Not wired into CI: this new directory is not in any job's test paths. #2539 / #2548 / #2549 each add toolshed/tests to the nightly tooling job with a byte-identical one-line change; extending that same line to pick this up would be trivial, but I left it out rather than create a four-way conflict on one line. Happy to fold it in whichever way you prefer.

Verification

  • pytest cuda_python_test_helpers/tests3 passed on macOS (the package imports only stdlib, so this runs natively).
  • With __init__.py restored from upstream/main, the module cannot even be imported on macOS, so the test file fails at collection with the OSError above. On a glibc host the meaningful teeth are in test_import_survives_without_the_glibc_soname, which fails there because the unconditional CDLL("libc.so.6") raises under the simulated block.
  • The restore was done with a try/finally harness (cp aside → git show upstream/main:<path> > → run → restore in finally), so the working tree is put back even when the run under test fails. Index verified clean before committing.
  • ruff check / ruff format --check clean on both files.

… libc.so.6

cuda_python_test_helpers/__init__.py loads the C runtime at import time:

    if IS_WINDOWS:
        libc = ctypes.CDLL("msvcrt.dll")
    else:
        libc = ctypes.CDLL("libc.so.6")

`libc.so.6` is the glibc soname specifically. It does not exist on musl
(Alpine) or on macOS, so importing the package raises OSError there:

    OSError: dlopen(libc.so.6, 0x0006): tried: 'libc.so.6' (no such file), ...

Three things make that worse than it looks:

* The module computes IS_LINUX correctly four lines above and then does not
  use it for this gate -- the else branch covers every non-Windows platform,
  not Linux.
* The library is needed by exactly one function: memcmp, used by
  cuda_core/tests/helpers/buffers.py. Everything else in the package
  (IS_WSL, IS_LINUX, under_compute_sanitizer, driver_version_less_than) is
  pure Python.
* The package is registered as a pytest plugin by cuda_core/tests/conftest.py
  via pytest_plugins, so the failure stops the whole suite from being
  *collected*, including every test that never touches libc.

Try the glibc soname first, so the library resolved on the platforms CI runs
on is unchanged, and fall back to ctypes.util.find_library("c"). If nothing
loads, raise an OSError naming the platform and what was tried instead of a
raw dlopen dump.

Note the package declares "Operating System :: POSIX :: Linux", so this is
arguably out of contract on macOS. It is not on musl, which is Linux and has
no libc.so.6 either -- and the cost of the current behaviour is that the
suite cannot be collected at all rather than skipping the one helper that
needs a C runtime.
@copy-pr-bot

copy-pr-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant